<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
    </style>
</head>

<body>
    <canvas id="canvas"></canvas>
</body>
<script id="ColorVertexShader" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    uniform float u_PointSize;
    uniform mat4 u_ModelM4;
    uniform mat4 u_PvM4;
    void main(){
        gl_Position = u_PvM4 * u_ModelM4 * a_Position;
        gl_PointSize = u_PointSize;
    }
</script>
<script id="ColorFragmentShader" type="x-shader/x-fragment">
    precision mediump float;
    uniform vec2 u_CanvasPosi;
    uniform float u_Set;
    void main(){
        gl_FragColor = vec4(gl_FragCoord.x / u_CanvasPosi.x,gl_FragCoord.y / u_CanvasPosi.y,u_Set,1);
    }
</script>
<script type="module">
    import { Matrix4, Vector3, Color, OrthographicCamera, PerspectiveCamera, Spherical, Plane, Ray } from '../js/three.module.js';
    import { CreateProgram, imgPromise } from '../js/Utils.js';
    import lib from '../js/ThreeLib.js';
    import CameraController from '../js/CameraController.js'
    import Poly from '../js/Poly4.js'

    import Geo from '../js/Poly/Geo.js'
    import Mat from '../js/Poly/Mat.js'
    import Obj3D from '../js/Poly/Obj3D.js'
    import Scene from '../js/Poly/Scene.js'



    //#region gl
    const canvas = document.getElementById("canvas");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight - 4;

    const gl = canvas.getContext("webgl", { antialias: true, });

    // gl.enable(gl.DEPTH_TEST);//开始背景遮挡会频闪
    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    // gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);//去图片描边
    //#endregion


    let program = CreateProgram(gl,
        document.querySelector('#ColorVertexShader').innerText,
        document.querySelector('#ColorFragmentShader').innerText);


    const scene = new Scene({ gl: gl });

    const pvM4 = new Matrix4();
    const modelM4 = new Matrix4();
    const eye = new Vector3(0, 0, 3);
    const lookAt = new Vector3(0, 0, 0);

    const camera = new PerspectiveCamera(45, canvas.width / canvas.height, 1, 20);
    const cameraController = new CameraController({
        canvas: canvas,
        camera: camera,
        pvMatrix: pvM4,
        rendEvent: draw,
        eye: eye,
        lookAt: lookAt,
    })

    const ray = new Ray(eye);
    let isInObj3D = null;

    const radius = 0.2;
    const nodeArr = [
        -1, 1, -1,
        -1, 1, 1,
        1, 1, 1,
        1, 1, -1,
        -1, -1, -1,
        -1, -1, 1,
        1, -1, 1,
        1, -1, -1,
    ];
    // const elementArr = [
    //     0, 1, 1, 2, 2, 3, 3, 0,
    //     4, 5, 5, 6, 6, 7, 7, 4,
    //     0, 4, 1, 5, 2, 6, 3, 7,
    //     0, 2, 1, 6, 5, 7, 4, 3, 0, 5, 2, 7
    // ];
    const elementArr = [
        0, 1, 2, 0, 2, 3,
        1, 5, 6, 1, 6, 2,
        5, 4, 7, 5, 7, 6,
        4, 0, 3, 4, 3, 7,
        2, 6, 7, 2, 7, 3,
        0, 4, 5, 0, 5, 1,
    ];

    create(getNodeArr(-1, -1, -1));
    create(getNodeArr(0, 0, 0));
    create(getNodeArr(1, 1, 1));
    draw();

    canvas.addEventListener('mousemove', function (event) {
        const { clientX, clientY } = event;
        const viewPosi = getViewPosi(clientX, clientY);

        ray.lookAt(viewPosi);
        getAllTRIANGLES();
    })

    function getNodeArr(x, y, z) {
        return [
            x - radius, y + radius, z - radius,
            x - radius, y + radius, z + radius,
            x + radius, y + radius, z + radius,
            x + radius, y + radius, z - radius,
            x - radius, y - radius, z - radius,
            x - radius, y - radius, z + radius,
            x + radius, y - radius, z + radius,
            x + radius, y - radius, z - radius,
        ];
    }

    function create(array) {
        const obj01 = new Obj3D({
            geo: new Geo({
                data: {
                    'a_Position': {
                        size: 3, array: new Float32Array(array)
                    },
                },
                element: {
                    array: new Uint8Array(elementArr)
                }
            }),
            mat: new Mat({
                program: CreateProgram(gl,
                    document.querySelector('#ColorVertexShader').innerText,
                    document.querySelector('#ColorFragmentShader').innerText),
                // mode: ['POINTS', 'LINE_LOOP'],
                mode: ['TRIANGLES'],
                data: {
                    'u_PvM4': { type: 'uniformMatrix4fv', value: pvM4.elements },
                    'u_ModelM4': { type: 'uniformMatrix4fv', value: modelM4.elements },
                    'u_PointSize': { type: 'uniform1f', value: 30 },
                    'u_Color': { type: 'uniform4fv', value: [1, 1, 1, 1], cRend: true },
                    'u_CanvasPosi': { type: 'uniform2fv', value: [canvas.width, canvas.height] },
                    'u_Set': { type: 'uniform1f', value: 0.5 },
                },
            })
        });
        scene.add(obj01);
    }

    function getAllTRIANGLES() {
        for (let u of scene.children) {
            let nodeArr = u.geo.data['a_Position'].array;
            let elementArr = u.geo.element.array;

            for (let i = 0; i < elementArr.length; i += 3) {
                let index1 = elementArr[i];
                let node1 = new Vector3(nodeArr[index1 * 3 + 0], nodeArr[index1 * 3 + 1], nodeArr[index1 * 3 + 2]);
                let index2 = elementArr[i + 1];
                let node2 = new Vector3(nodeArr[index2 * 3 + 0], nodeArr[index2 * 3 + 1], nodeArr[index2 * 3 + 2]);
                let index3 = elementArr[i + 2];
                let node3 = new Vector3(nodeArr[index3 * 3 + 0], nodeArr[index3 * 3 + 1], nodeArr[index3 * 3 + 2]);

                let inT = inTRIANGLES(ray,
                    [
                        node1.clone().applyMatrix4(modelM4),
                        node2.clone().applyMatrix4(modelM4),
                        node3.clone().applyMatrix4(modelM4)
                    ]
                );
                if (inT != null) {
                    isInObj3D = u;
                    return;
                }
            }
        }
        isInObj3D = null;
    }

    function inTRIANGLES(ray, [A, B, C]) {
        const E = ray.origin;
        const V = ray.direction;

        const F = A.clone().sub(B).cross(B.clone().sub(C));

        // (M-A) · F = 0
        // j * V = E - M
        // M = j * V + E

        // ((j * V + E) - A) · F = 0
        // jV · F  + (E - A) · F = 0
        // jV · F = (A - E) · F
        // j = ((A - E) · F) / (V · F)
        // M = (((A - E) · F) / (V · F)) * V + E

        const M = V.clone().multiplyScalar((A.clone().sub(E).dot(F) / V.clone().dot(F))).add(E);

        let arr = [A, B, C];
        for (let i = 0; i < 3; i++) {
            let a = arr[i];
            let b = arr[(i + 1) % 3];
            let ma = a.clone().sub(M);
            let ab = b.clone().sub(a);
            let mF = ma.clone().cross(ab);
            if (mF.dot(F) < 0) {
                return null;
            }
        }
        return M;
    }

    function getViewPosi(clientX, clientY) {
        const [hx, hy] = [canvas.width / 2, canvas.height / 2];
        const [x, y] = [(clientX - hx) / hx, -(clientY - hy) / hy]
        return new Vector3(x, y, 0).applyMatrix4(pvM4.clone().invert());
    }

    function draw() {
        scene.setUniform("u_PvM4", { value: pvM4.elements });
        scene.draw();
    }


    (function ani(time) {
        if (isInObj3D != null) {
            isInObj3D.setUniform('u_Set', { value: (Math.sin(time / 200) + 1) / 2 })
        }
        draw();
        requestAnimationFrame(ani);
    })();

</script>

</html>